/*
 * QTeXPreview
 * Copyright (C) 2008, Philip Pronin (philip.pronin@gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "editor.h"
#include "settings.h"

#include <QStack>

TeXEditor::TeXEditor(QWidget* parent) : QTextEdit(parent), highlighter(NULL) {
	setAcceptRichText(false);

	applySettings();
	highlighter = new TeXSyntaxHighlighter(this);

	connect(this, SIGNAL(textChanged()), SLOT(checkMatching()));
	connect(this, SIGNAL(cursorPositionChanged()), SLOT(checkMatching()));
}

QString TeXEditor::text() const {
	return toPlainText();
}

void TeXEditor::setText(const QString& str) {
	setPlainText(str);
}

bool TeXEditor::getSyntaxHighlightingEnabled() const {
	return syntaxHighlighting;
}

void TeXEditor::setSyntaxHighlightingEnabled(bool enable) {
	syntaxHighlighting = enable;
	highlighter->rehighlight();
	checkMatching();
}

bool TeXEditor::getBracketsMathingEnabled() const {
	return bracketsMathing;
}

void TeXEditor::setBracketsMathingEnabled(bool enable) {
	bracketsMathing = enable;
	checkMatching();
}

void TeXEditor::applySettings() {
	Settings* settings = Settings::getInstance();
	syntaxHighlighting = settings->getSyntaxHighlighting();
	bracketsMathing = settings->getBracketsMatching();
	if (highlighter) {
		highlighter->rehighlight();
		checkMatching();
	}
}

void TeXEditor::checkMatching() {
	if (!getSyntaxHighlightingEnabled() || !getBracketsMathingEnabled()) {
		setExtraSelections(QList<ExtraSelection>());
		return;
	}

	QMap<QChar, QChar> brMap;
	brMap['('] = ')'; brMap['['] = ']'; brMap['{'] = '}';

	QString txt = text();
	QStack<QPair<int, QChar> > brackets;
	QList<int> bad; int actStart = -1, actEnd = -1;

	for (int i = 0, cur = textCursor().position(); i < txt.size(); i++)
		if (brMap.contains(txt[i])) brackets.push(qMakePair(i, (QChar)txt[i]));
		else if (brMap.values().contains(txt[i])) {
			if (brackets.empty() || brMap[brackets.top().second] != txt[i])
				bad.push_back(i);
			else {
				if (actStart == -1 && i >= cur && brackets.top().first < cur) {
					actStart = brackets.top().first;
					actEnd = i;
				}
				brackets.pop();
			}
		}
	while (!brackets.empty()) bad.push_back(brackets.pop().first);

	QList<ExtraSelection> extras;

	#define ES(pos, color) { \
		ExtraSelection _es; \
		_es.cursor = textCursor(); \
		_es.cursor.setPosition(pos); \
		_es.cursor.setPosition(pos + 1, QTextCursor::KeepAnchor); \
		_es.format.setForeground(color); \
		extras << _es; }

	foreach (int i, bad) ES(i, Qt::red)

	if (actStart != -1) {
		ES(actStart, Qt::blue)
		ES(actEnd, Qt::blue)
	}

	#undef ES

	setExtraSelections(extras);
}

TeXSyntaxHighlighter::TeXSyntaxHighlighter(TeXEditor* parent) : QSyntaxHighlighter(parent) {
	this->parent = parent;

	keywordFormat.setFontWeight(QFont::Bold);
	keywordFormat.setForeground(Qt::darkBlue);

	bracketsFormat.setFontWeight(QFont::Bold);
}

void TeXSyntaxHighlighter::highlightBlock(const QString& text) {
	if (!parent->getSyntaxHighlightingEnabled()) return;

	QString patternBrackets = "[(){}\\[\\]]";
	QRegExp bracketsRE(patternBrackets);
	int index = text.indexOf(bracketsRE);
	while (index >= 0) {
		int length = bracketsRE.matchedLength();
		setFormat(index, length, bracketsFormat);
		index = text.indexOf(bracketsRE, index + length);
	}

	QString patternKeyword = "\\\\[A-Za-z]*|\\\\.";
	QRegExp keywordRE(patternKeyword);
	index = text.indexOf(keywordRE);
	while (index >= 0) {
		int length = keywordRE.matchedLength();
		setFormat(index, length, keywordFormat);
		index = text.indexOf(keywordRE, index + length);
	}
}
